#ifndef CLIENT_SQL_STRING_INCLUDED
#define CLIENT_SQL_STRING_INCLUDED

/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */

/* This file is originally from the mysql distribution. Coded by monty */

#include "m_ctype.h"
#include "my_sys.h"

class String;
int sortcmp(const String *a, const String *b, const CHARSET_INFO *cs);
String *copy_if_not_alloced(String *a, String *b, uint32 arg_length);
inline uint32 copy_and_convert(char *to, uint32 to_length,
                               const CHARSET_INFO *to_cs,
                               const char *from, uint32 from_length,
                               const CHARSET_INFO *from_cs, uint *errors)
{
    return my_convert(to, to_length, to_cs, from, from_length, from_cs, errors);
}

class String
{
    char *Ptr;
    uint32 str_length, Alloced_length;
    bool alloced;
    const CHARSET_INFO *str_charset;
public:
    String()
    {
        Ptr = 0; str_length = Alloced_length = 0; alloced = 0;
        str_charset = &my_charset_bin;
    }
    String(uint32 length_arg)
    {
        alloced = 0; Alloced_length = 0; (void) real_alloc(length_arg);
        str_charset = &my_charset_bin;
    }
    String(const char *str, const CHARSET_INFO *cs)
    {
        Ptr = (char *) str; str_length = (uint) strlen(str); Alloced_length = 0; alloced = 0;
        str_charset = cs;
    }
    String(const char *str, uint32 len, const CHARSET_INFO *cs)
    {
        Ptr = (char *) str; str_length = len; Alloced_length = 0; alloced = 0;
        str_charset = cs;
    }
    String(char *str, uint32 len, const CHARSET_INFO *cs)
    {
        Ptr = (char *) str; Alloced_length = str_length = len; alloced = 0;
        str_charset = cs;
    }
    String(const String &str)
    {
        Ptr = str.Ptr ; str_length = str.str_length ;
        Alloced_length = str.Alloced_length; alloced = 0;
        str_charset = str.str_charset;
    }
    static void *operator new(size_t size, MEM_ROOT *mem_root)
    {
        return (void *) alloc_root(mem_root, (uint) size);
    }
    static void operator delete(void *ptr_arg, size_t size)
    {
        (void) ptr_arg;
        (void) size;
        TRASH(ptr_arg, size);
    }
    static void operator delete(void *, MEM_ROOT *)
    { /* never called */ }
    ~String() {
        free();
    }

    inline void set_charset(const CHARSET_INFO *charset_arg)
    {
        str_charset = charset_arg;
    }
    inline const CHARSET_INFO *charset() const {
        return str_charset;
    }
    inline uint32 length() const {
        return str_length;
    }
    inline uint32 alloced_length() const {
        return Alloced_length;
    }
    inline char &operator [] (uint32 i) const {
        return Ptr[i];
    }
    inline void length(uint32 len) {
        str_length = len ;
    }
    inline bool is_empty() {
        return (str_length == 0);
    }
    inline void mark_as_const() {
        Alloced_length = 0;
    }
    inline const char *ptr() const {
        return Ptr;
    }
    inline char *c_ptr()
    {
        DBUG_ASSERT(!alloced || !Ptr || !Alloced_length ||
                    (Alloced_length >= (str_length + 1)));

        if (!Ptr || Ptr[str_length])		/* Should be safe */
            (void) realloc(str_length);

        return Ptr;
    }
    inline char *c_ptr_quick()
    {
        if (Ptr && str_length < Alloced_length)
            Ptr[str_length] = 0;

        return Ptr;
    }
    inline char *c_ptr_safe()
    {
        if (Ptr && str_length < Alloced_length)
            Ptr[str_length] = 0;
        else
            (void) realloc(str_length);

        return Ptr;
    }

    void set(String &str, uint32 offset, uint32 arg_length)
    {
        DBUG_ASSERT(&str != this);
        free();
        Ptr = (char *) str.ptr() + offset; str_length = arg_length; alloced = 0;

        if (str.Alloced_length)
            Alloced_length = str.Alloced_length - offset;
        else
            Alloced_length = 0;

        str_charset = str.str_charset;
    }
    inline void set(char *str, uint32 arg_length, const CHARSET_INFO *cs)
    {
        free();
        Ptr = (char *) str; str_length = Alloced_length = arg_length ; alloced = 0;
        str_charset = cs;
    }
    inline void set(const char *str, uint32 arg_length, const CHARSET_INFO *cs)
    {
        free();
        Ptr = (char *) str; str_length = arg_length; Alloced_length = 0 ; alloced = 0;
        str_charset = cs;
    }
    bool set_ascii(const char *str, uint32 arg_length);
    inline void set_quick(char *str, uint32 arg_length, const CHARSET_INFO *cs)
    {
        if (!alloced)
        {
            Ptr = (char *) str; str_length = Alloced_length = arg_length;
        }

        str_charset = cs;
    }
    bool set(longlong num, const CHARSET_INFO *cs);
    bool set(ulonglong num, const CHARSET_INFO *cs);
    bool set(double num, uint decimals, const CHARSET_INFO *cs);

    /*
      PMG 2004.11.12
      This is a method that works the same as perl's "chop". It simply
      drops the last character of a string. This is useful in the case
      of the federated storage handler where I'm building a unknown
      number, list of values and fields to be used in a sql insert
      statement to be run on the remote server, and have a comma after each.
      When the list is complete, I "chop" off the trailing comma

      ex.
        String stringobj;
        stringobj.append("VALUES ('foo', 'fi', 'fo',");
        stringobj.chop();
        stringobj.append(")");

      In this case, the value of string was:

      VALUES ('foo', 'fi', 'fo',
      VALUES ('foo', 'fi', 'fo'
      VALUES ('foo', 'fi', 'fo')

    */
    inline void chop()
    {
        Ptr[str_length--] = '\0';
    }

    inline void free()
    {
        if (alloced)
        {
            alloced = 0;
            Alloced_length = 0;
            my_free(Ptr);
            Ptr = 0;
            str_length = 0;				/* Safety */
        }
    }
    inline bool alloc(uint32 arg_length)
    {
        if (arg_length < Alloced_length)
            return 0;

        return real_alloc(arg_length);
    }
    bool real_alloc(uint32 arg_length);			// Empties old string
    bool realloc(uint32 arg_length);

    // Shrink the buffer, but only if it is allocated on the heap.
    inline void shrink(uint32 arg_length)
    {
        if (!is_alloced())
            return;

        if (arg_length < Alloced_length)
        {
            char *new_ptr;

            if (!(new_ptr = (char *) my_realloc(Ptr, arg_length, MYF(0))))
            {
                Alloced_length = 0;
                real_alloc(arg_length);
            }

            else
            {
                Ptr = new_ptr;
                Alloced_length = arg_length;
            }
        }
    }
    bool is_alloced() const {
        return alloced;
    }
    inline String &operator = (const String &s)
    {
        if (&s != this)
        {
            /*
              It is forbidden to do assignments like
              some_string = substring_of_that_string
             */
            DBUG_ASSERT(!s.uses_buffer_owned_by(this));
            free();
            Ptr = s.Ptr ; str_length = s.str_length ; Alloced_length = s.Alloced_length;
            alloced = 0;
        }

        return *this;
    }

    bool copy();					// Alloc string if not alloced
    bool copy(const String &s);			// Allocate new string
    /// Allocate new string
    bool copy(const char *s, uint32 arg_length, const CHARSET_INFO *cs);
    static bool needs_conversion(uint32 arg_length,
                                 const CHARSET_INFO *cs_from, const CHARSET_INFO *cs_to,
                                 uint32 *offset);
    bool copy_aligned(const char *s, uint32 arg_length, uint32 offset,
                      const CHARSET_INFO *cs);
    bool set_or_copy_aligned(const char *s, uint32 arg_length,
                             const CHARSET_INFO *cs);
    bool copy(const char *s, uint32 arg_length, const CHARSET_INFO *csfrom,
              const CHARSET_INFO *csto, uint *errors);
    bool append(const String &s);
    bool append(const char *s);
    bool append(const char *s, uint32 arg_length);
    bool append(const char *s, uint32 arg_length, const CHARSET_INFO *cs);
    bool append(IO_CACHE *file, uint32 arg_length);
    bool append_with_prefill(const char *s, uint32 arg_length,
                             uint32 full_length, char fill_char);
    int strstr(const String &search, uint32 offset = 0); // Returns offset to substring or -1
    int strrstr(const String &search, uint32 offset = 0); // Returns offset to substring or -1
    bool replace(uint32 offset, uint32 arg_length, const char *to, uint32 length);
    bool replace(uint32 offset, uint32 arg_length, const String &to);
    inline bool append(char chr)
    {
        if (str_length < Alloced_length)
            Ptr[str_length++] = chr;
        else
        {
            if (realloc(str_length + 1))
                return 1;

            Ptr[str_length++] = chr;
        }

        return 0;
    }
    bool fill(uint32 max_length, char fill);
    void strip_sp();
    friend int sortcmp(const String *a, const String *b, const CHARSET_INFO *cs);
    friend int stringcmp(const String *a, const String *b);
    friend String *copy_if_not_alloced(String *a, String *b, uint32 arg_length);
    uint32 numchars() const;
    int charpos(int i, uint32 offset = 0);

    int reserve(uint32 space_needed)
    {
        return realloc(str_length + space_needed);
    }
    int reserve(uint32 space_needed, uint32 grow_by);

    /*
      The following append operations do NOT check alloced memory
      q_*** methods writes values of parameters itself
      qs_*** methods writes string representation of value
    */
    void q_append(const char c)
    {
        Ptr[str_length++] = c;
    }
    void q_append(const uint32 n)
    {
        int4store(Ptr + str_length, n);
        str_length += 4;
    }
    void q_append(double d)
    {
        float8store(Ptr + str_length, d);
        str_length += 8;
    }
    void q_append(double *d)
    {
        float8store(Ptr + str_length, *d);
        str_length += 8;
    }
    void q_append(const char *data, uint32 data_len)
    {
        memcpy(Ptr + str_length, data, data_len);
        str_length += data_len;
    }

    void write_at_position(int position, uint32 value)
    {
        int4store(Ptr + position, value);
    }

    void qs_append(const char *str, uint32 len);
    void qs_append(double d);
    void qs_append(double *d);
    inline void qs_append(const char c)
    {
        Ptr[str_length] = c;
        str_length++;
    }
    void qs_append(int i);
    void qs_append(uint i);

    /* Inline (general) functions used by the protocol functions */

    inline char *prep_append(uint32 arg_length, uint32 step_alloc)
    {
        uint32 new_length = arg_length + str_length;

        if (new_length > Alloced_length)
        {
            if (realloc(new_length + step_alloc))
                return 0;
        }

        uint32 old_length = str_length;
        str_length += arg_length;
        return Ptr + old_length;			/* Area to use */
    }

    inline bool append(const char *s, uint32 arg_length, uint32 step_alloc)
    {
        uint32 new_length = arg_length + str_length;

        if (new_length > Alloced_length && realloc(new_length + step_alloc))
            return TRUE;

        memcpy(Ptr + str_length, s, arg_length);
        str_length += arg_length;
        return FALSE;
    }
    void print(String *print);

    /* Swap two string objects. Efficient way to exchange data without memcpy. */
    void swap(String &s);

    inline bool uses_buffer_owned_by(const String *s) const
    {
        return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
    }
};

#endif /* CLIENT_SQL_STRING_INCLUDED */
